package com.sky.aspect;

import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * 自定义切面类，实现公共字段自动填充处理逻辑
 */
@Aspect // 声明当前类为切面类
@Component
@Slf4j
public class AutoFillAspect {
    /**
     * 切入点
     */
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill))")
    public void autoFillPointCut() {
    }

    /**
     * 前置通知，在通知中进行公共字段的赋值
     */
    @Before("autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint) {
        log.info("开始进行公共字段自动填充......");
        // 获取当前被拦截的方法的签名对象
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取当前被拦截方法的注解对象
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
        // 获取注解对象的值（数据库操作类型）
        OperationType operationType = autoFill.value();
        // 获取当前被拦截方法的参数——实体对象
        Object[] args = joinPoint.getArgs();
        // 如果没有传递参数，则终止函数执行
        if (args == null || args.length == 0) return;
        // 如果有传递参数，则获取第一个参数的值
        Object entity = args[0];
        // 准备公共字段“修改时间”的数据
        LocalDateTime currentTime = LocalDateTime.now();
        // 准备公共字段“修改人”的数据
        Long currentUserId = BaseContext.getCurrentId();
        // 获取被拦截方法参数（实体对象）的实体类
        Class<?> currentEntityClass = entity.getClass();
        try {
            // 如果是“新增”类型的操作
            if (operationType == OperationType.INSERT) {
                // 通过实体类获取公共字段“创建时间”的设置方法
                Method setCreateTime = currentEntityClass.getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
                // 通过实体类获取公共字段“创建人”的设置方法
                Method setCreateUser = currentEntityClass.getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
                // 通过实体类获取公共字段“修改时间”的设置方法
                Method setUpdateTime = currentEntityClass.getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                // 通过实体类获取公共字段“修改人”的设置方法
                Method setUpdateUser = currentEntityClass.getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
                // 通过反射为指定对象的公共字段"创建时间"赋值
                setCreateTime.invoke(entity, currentTime);
                // 通过反射为指定对象的公共字段"创建人"赋值
                setCreateUser.invoke(entity, currentUserId);
                // 通过反射为指定对象的公共字段"修改时间"赋值
                setUpdateTime.invoke(entity, currentTime);
                // 通过反射为指定对象的公共字段"修改人"赋值
                setUpdateUser.invoke(entity, currentUserId);
            } else if (operationType == OperationType.UPDATE) {// 如果是“修改”类型的操作
                // 通过实体类获取公共字段“修改时间”的设置方法
                Method setUpdateTime = currentEntityClass.getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                // 通过实体类获取公共字段“修改人”的设置方法
                Method setUpdateUser = currentEntityClass.getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
                // 通过反射为指定对象的公共字段"修改时间"赋值
                setUpdateTime.invoke(entity, currentTime);
                // 通过反射为指定对象的公共字段"修改人"赋值
                setUpdateUser.invoke(entity, currentUserId);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
